﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTFF.WPF.Model.GlobalHotKeys;
using System.Windows.Input;
using iTFF.WPF.CP;

namespace iTFF.WPF.ViewModel
{
    /// <summary>
    /// A friendly
    /// </summary>
    class GlobalHotKeyViewModel : ViewModelBase
    {
        #region Fields

        GlobalHotKey _hotKey;
        int _displayIndex;
        bool _isWinKeyPressed;
        bool _canEdit;
        Key _bakKey;
        ModifierKeys _bakMod;
        RelayCommand<KeyEventArgs> _previewkeyDownCommand;
        RelayCommand _applyCommand;
        RelayCommand _cancelCommand;

        #endregion

        #region Constructor

        public GlobalHotKeyViewModel(GlobalHotKey hotkey, int displayIndex)
        {

            if (hotkey == null)
                throw new ArgumentNullException("hotkey");

            _hotKey = hotkey;
            _displayIndex = displayIndex;

            base.DisplayName = hotkey.DisplayText;

            IsWinKeyPressed = (this.Modifiers | ModifierKeys.Windows) == this.Modifiers;

            hotkey.IsRegisteredChanged += IsRegisteredChanged;

        }

        void IsRegisteredChanged(object sender, IsRegisteredEventArgs e)
        {
            // refresh the isvalid value when a shortcut is registered/unregistered
            OnPropertyChanged("IsInvalid");
        }

        #endregion

        #region Base Properties

        public ModifierKeys Modifiers
        {
            get { return _hotKey.Modifiers; }
            set
            {
                if (_hotKey.Modifiers != value)
                {
                    _hotKey.Modifiers = value;

                    OnPropertyChanged("Modifiers");
                    OnPropertyChanged("KeysCombination");
                }
            }
        }

        public Key Key
        {
            get { return _hotKey.Key; }
            set
            {
                if (_hotKey.Key != value)
                {
                    _hotKey.Key = value;

                    OnPropertyChanged("Keys");
                    OnPropertyChanged("KeysCombination");
                }
            }
        }

        public string Name
        {
            get { return _hotKey.Name; }
        }

        public bool HasSeparatorBefore
        {
            get { return _hotKey.HasSeparatorBefore; }
        }

        #endregion

        #region Base private Properties

        private int HotKeyID
        {
            get { return _hotKey.HotKeyID; }
        }

        private bool IsSet
        {
            get { return _hotKey.IsSet; }
        }

        private bool IsRegistered
        {
            get { return _hotKey.IsRegistered; }
        }

        #endregion

        #region Presentation properties

        /// <summary>
        /// Represents the key combination as a string
        /// </summary>
        public string KeysCombination
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                if (Modifiers != ModifierKeys.None)
                    sb.Append(Modifiers.ToString());

                if (Modifiers != ModifierKeys.None && Key != Key.None)
                    sb.Append(" + ");

                if (Key != Key.None)
                    sb.Append(Key.ToString());

                return sb.ToString();
            }
        }

        /// <summary>
        /// Indicates if the Win key is in the modifiers
        /// </summary>
        public bool IsWinKeyPressed
        {
            get { return _isWinKeyPressed; }
            set
            {
                if (_isWinKeyPressed != value)
                {
                    _isWinKeyPressed = value;

                    // refresh the modifiers
                    if (_isWinKeyPressed && (Modifiers | ModifierKeys.Windows) != Modifiers)
                        Modifiers |= ModifierKeys.Windows;
                    else if (!_isWinKeyPressed && (Modifiers | ModifierKeys.Windows) == Modifiers)
                        Modifiers ^= ModifierKeys.Windows;

                    OnPropertyChanged("IsWinKeyPressed");
                }
            }
        }

        /// <summary>
        /// Indicates if the Key can be edited
        /// </summary>
        public bool CanEdit
        {
            get { return _canEdit; }
            set
            {
                if (_canEdit != value)
                {
                    _canEdit = value;

                    if (_canEdit)
                        BackupKeys();
                    OnPropertyChanged("CanEdit");
                    OnPropertyChanged("IsInvalid");
                }
            }
        }

        /// <summary>
        /// Indicates the HotKey is invalid
        /// </summary>
        public bool IsInvalid
        {
            get
            {
                return IsSet && !IsRegistered && !CanEdit;
            }
        }

        #endregion

        #region Commands

        public ICommand PreviewKeyDownCommand
        {
            get
            {
                // the key down is handled only if CanEdit is true
                if (_previewkeyDownCommand == null)
                    _previewkeyDownCommand = new RelayCommand<KeyEventArgs>(param => OnPreviewKeyDown(param), param => CanEdit);
                return _previewkeyDownCommand;
            }
        }

        private void OnPreviewKeyDown(KeyEventArgs e)
        {
            e.Handled = true;

            // set the modifiers, don't forget the win key
            switch (e.Key)
            {
                case Key.LWin:
                case Key.RWin:

                    this.Modifiers = e.KeyboardDevice.Modifiers | ModifierKeys.Windows;
                    this.IsWinKeyPressed = true;

                    break;

                case Key.Escape:

                    this.Modifiers = ModifierKeys.None;
                    this.Key = Key.None;

                    break;

                default:

                    if (this.IsWinKeyPressed)
                        this.Modifiers = e.KeyboardDevice.Modifiers | ModifierKeys.Windows;
                    else
                        this.Modifiers = e.KeyboardDevice.Modifiers;
                    this.Key = e.Key == Key.System ? e.SystemKey : e.Key;
                    break;

            }

        }

        public ICommand ApplyCommand
        {
            get 
            {
                if (_applyCommand == null)
                    _applyCommand = new RelayCommand(param => OnApply());
                return _applyCommand;
            }
        }

        public ICommand CancelCommand
        {
            get
            {
                if (_cancelCommand == null)
                    _cancelCommand = new RelayCommand(param => OnCancel());
                return _cancelCommand;
            }
        }

        #endregion

        #region Register / Unregister

        /// <summary>
        /// Backup the Keys and Modifiers in case of Canceling
        /// </summary>
        private void BackupKeys()
        {
            _bakKey = Key;
            _bakMod = Modifiers;
        }

        /// <summary>
        /// Applies the Hot Key. Unregisters the old one and registers the new.
        /// </summary>
        /// <returns></returns>
        private void OnApply()
        {
            // unregister
            if (HotKeyID != -1)
                ControlPanel.Instance.GlobalHotKeysControlPanel.Unregister(HotKeyID);

            // register
            if (IsSet)
            {
                try
                {
                    ControlPanel.Instance.GlobalHotKeysControlPanel.Register(_hotKey);
                }
                catch (ApplicationException)
                {
                    // don't do nothing if the shortcut hasn't been save, the presentation property IsValid will do the job
                }
            }
        }

        /// <summary>
        /// Restore the Hot key to its old values
        /// </summary>
        /// <returns></returns>
        private void OnCancel()
        {
            Key = _bakKey;
            Modifiers = _bakMod;
        }

        #endregion

    }
}
